package business;

import java.util.ArrayList;
import java.util.List;

import persistence.CostUnit;
import persistence.CostUnitDAO;
import persistence.LibraryLocation;
import persistence.PredefinedEntry;
import persistence.PredefinedEntryDAO;
import persistence.PredefinedParamDAO;
import persistence.Purchaser;
import persistence.PurchaserDAO;
import persistence.Subscription;
import persistence.SubscriptionDAO;
import persistence.Supplier;
import persistence.SupplierDAO;
import persistence.exception.DAOException;
import business.exception.BusinessException;
import business.validation.BusinessValidator;
import business.validation.ValidationException;

public class SubscriptionLogicImpl implements SubscriptionLogic {
	private CommonLogic commonLogic;
	private SubscriptionDAO subscriptionDAO;
	private PredefinedEntryDAO predefinedEntryDAO;
	private PredefinedParamDAO predefinedParamDAO;
	private CostUnitDAO costUnitDAO;
	private PurchaserDAO purchaserDAO;
	private SupplierDAO supplierDAO;
	private UserManager userManager;
	private BusinessValidator<Subscription> validator;
	
	public SubscriptionLogicImpl(SubscriptionDAO subscriptionDAO, PredefinedEntryDAO predefinedEntryDAO, PredefinedParamDAO predefinedParamDAO, CostUnitDAO costUnitDAO, PurchaserDAO purchaserDAO, SupplierDAO supplierDAO, UserManager userManager, CommonLogic commonLogic, BusinessValidator<Subscription> validator) {		
		this.subscriptionDAO = subscriptionDAO;
		this.predefinedEntryDAO = predefinedEntryDAO;
		this.predefinedParamDAO = predefinedParamDAO;
		this.costUnitDAO = costUnitDAO;
		this.purchaserDAO = purchaserDAO;
		this.supplierDAO = supplierDAO;
		this.userManager = userManager;
		this.commonLogic = commonLogic;
		this.validator = validator;
	}	
	
	public SubscriptionLogicImpl() {}

	public SubscriptionDAO getSubscriptionDAO() {
		return subscriptionDAO;
	}

	public void setSubscriptionDAO(SubscriptionDAO subscriptionDAO) {
		this.subscriptionDAO = subscriptionDAO;
	}	

	public PredefinedEntryDAO getPredefinedEntryDAO() {
		return predefinedEntryDAO;
	}

	public void setPredefinedEntryDAO(PredefinedEntryDAO predefinedEntryDAO) {
		this.predefinedEntryDAO = predefinedEntryDAO;
	}
	
	public void setPredefinedParamDAO(PredefinedParamDAO predefParamDAO) {
		this.predefinedParamDAO = predefParamDAO;		
	}

	public PredefinedParamDAO getPredefinedParamDAO() {
		return predefinedParamDAO;
	}

	public CostUnitDAO getCostUnitDAO() {
		return costUnitDAO;
	}

	public void setCostUnitDAO(CostUnitDAO costUnitDAO) {
		this.costUnitDAO = costUnitDAO;
	}	

	public PurchaserDAO getPurchaserDAO() {
		return purchaserDAO;
	}

	public void setPurchaserDAO(PurchaserDAO purchaserDAO) {
		this.purchaserDAO = purchaserDAO;
	}
	
	public SupplierDAO getSupplierDAO() {
		return supplierDAO;
	}

	public void setSupplierDAO(SupplierDAO supplierDAO) {
		this.supplierDAO = supplierDAO;
	}

	public UserManager getUserManager() {
		return userManager;
	}

	public void setUserManager(UserManager userManager) {
		this.userManager = userManager;
	}
	
	public CommonLogic getCommonLogic() {
		return commonLogic;
	}
	
	public void setCommonLogic(CommonLogic commonLogic) {
		this.commonLogic = commonLogic;
	}

	public void setBusinessValidator(BusinessValidator<Subscription> validator) {
		this.validator = validator;
	}

	public BusinessValidator<Subscription> getBusinessValidator() {
		return validator;
	}

	@Override
	public List<Subscription> getAllSubscriptionsWithTitleForLibraryLocation(String title, LibraryLocation location) throws BusinessException{
		System.out.println("<<<<<<<<<<<<<<<< ENTERING getAllSubsriptionsWithTitle");
		List<Subscription> subscriptions = null;
		try {
				subscriptions = this.subscriptionDAO.getAllSubscriptionsForLocation(location);

// Produces ConcurrentModificationException on the list				
//				for (Subscription s : subscriptions) {
//					System.out.println("PERIODICAL TITLE: " + s.getPeriodical().getTitle());
//					if (!s.getPeriodical().getTitle().toUpperCase().startsWith(title.toUpperCase())) {
//						subscriptions.remove(s);
//					}
//				}
// Produces wrong output !!!:
//				for (int i = 0; i < subscriptions.size(); i++) {
//					Subscription currentSubscription = subscriptions.get(i);
//					if (!currentSubscription.getPeriodical().getTitle().toUpperCase().startsWith(title.toUpperCase())) {
//						System.out.println("REMOVED: " + subscriptions.remove(currentSubscription));
//					}
//				}
				/*
				 * Avoids a ConcurrentModificationException
				 */
				Object subscriptionArray[] = subscriptions.toArray();
				subscriptions = new ArrayList<Subscription>();
				
				for (Object o : subscriptionArray) {
					Subscription currentSubscription = (Subscription)o;
					if (currentSubscription.getPeriodical().getTitle().toUpperCase().startsWith(title.toUpperCase())) {
						subscriptions.add(currentSubscription);
					}
				}
		}
		catch (DAOException de) {
			throw new BusinessException("Error when attempting to find all subscriptions whose title starts with: " + title, de);
		}
		return subscriptions;
	}

	@Override
	public List<Subscription> getAllSubscriptionsWithTitle(String title)
			throws BusinessException {
		List<Subscription> subscriptions = null;
		try {
			subscriptions = subscriptionDAO.getAllSubscriptionsByTitle(title);
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to get all subscriptions", de);
		}
		return subscriptions;
	}
	
	@Override
	public Subscription findGivenSubscription(int id) throws BusinessException{
		try {
			Subscription retVal = this.subscriptionDAO.findSubscription(id);
			return retVal;
		}
		catch (DAOException de) {
			throw new BusinessException("Error when attempting to find Subscription with id: " + id, de);
		}
	}

	@Override
	public List<Subscription> getAllSubscriptions() throws BusinessException {
		try {
			return this.subscriptionDAO.getAllSubscriptionsAsc();
		}
		catch (DAOException de) {
			throw new BusinessException("Error when attempting to get all subscriptions in ASC order", de);
		}
	}

	@Override
	public List<Subscription> getAllSubscriptionsForLibraryLocation(LibraryLocation location) throws BusinessException {
		return getAllSubscriptionsWithTitleForLibraryLocation("", location);
	}

	@Override
	public Subscription getNewEmptySubscription() {
		return subscriptionDAO.createSubscription();
	}	

	@Override
	public Subscription saveNewSubscription(Subscription sub)
			throws BusinessException, ValidationException {
		Subscription createdSubscription = null;
		try {
//			this.subscriptionDAO.createSubscription(sub.getId(), sub.getSubscriptionNo(), sub.getSupplier(), sub.getStatus(), sub.getCurrentPrice(),
//													sub.getPreviousPrice(), sub.getInternalLocation(), sub.getLocation(), sub.getKindOfAcquisition(), sub.getOrderingDate(),
//													sub.getStartDate(), sub.getCurrentIssueDate(), sub.getEndDate(), sub.getPaidUntil(), sub.getCancellationPeriod(),
//													sub.isOnlineInAleph(), sub.getCostUnit(), sub.getPurchaser(), sub.getBookbindingDetails(), sub.getSapAccountsPayableNo(),
//													sub.getSignature(), sub.getBarcode(), sub.getNotes(), sub.getOnlineNotes(), sub.getCurrency(),
//													sub.getKindOfStorage(), sub.getPeriodical(), sub.getReminderFrequency());
			/* insert correct Subscription via UI and modifiy it to be incorrect, otherwise JSR303
			 * won't let the subscription be inserted
			 * why? To ensure, other clients than JSF (f.e. Web-Services) can't enter invalid
			 * data
			 */
			///ILLEGAL - move to tests
//			sub.setCostUnit(null);
			////////////////////// factored out validation logic in separate method validate()
			validator.validate(sub);
			////////////////////
			createdSubscription = this.subscriptionDAO.saveSubscription(sub);
		}
		catch (DAOException de) {
			throw new BusinessException("Error storing subscription: " + sub, de);
		}
		return createdSubscription;
	}
	
	@Override
	public List<Purchaser> getAllPurchasers() throws BusinessException {
		List<Purchaser> purchasers = null;
		try {
			purchasers = this.purchaserDAO.findAll();
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to get all Purchasers", de);
		}
		return purchasers;
	}
	
	@Override
	public List<Purchaser> getAllPurchasersForLibraryLocation(LibraryLocation libraryLocation) throws BusinessException {
		List<Purchaser> purchasers = null;
		try {
			purchasers = this.purchaserDAO.findAllForLocation(libraryLocation);
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to find alls purchasers for location with id: " + libraryLocation.getId(), de);
		}
		return purchasers;
	}

	@Override
	public List<PredefinedEntry> getSubscriptionStates() throws BusinessException {
		List<PredefinedEntry> subscriptionStates = null;
		subscriptionStates = commonLogic.findEntriesForParam(SUBSCRIPTION_STATUS_PARAM);
		return subscriptionStates;
	}
	
	public List<PredefinedEntry> getAllKindsOfAcquisition() throws BusinessException {
		List<PredefinedEntry> kindsOfAcquisition = null;
		kindsOfAcquisition = commonLogic.findEntriesForParam(KIND_OF_ACQUISITION_PARAM);
		return kindsOfAcquisition;
	}
	
	public List<CostUnit> getCostUnits(LibraryLocation location) throws BusinessException {
		List<CostUnit> costUnits = null;
		
		try {
			costUnits = costUnitDAO.findAllForLocation(location);
		}
		catch (DAOException de) {
			throw new BusinessException("Error when attempting to get all cost units for location with id: " + location.getId(), de);
		}
		return costUnits;
	}
	
	public List<CostUnit> getCostUnits() throws BusinessException {
		List<CostUnit> costUnits = null;
		
		try{
			costUnits = costUnitDAO.findAll();
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to get all cost units", de);
		}
		return costUnits;
	}

	@Override
	public List<Supplier> getAllSuppliers() throws BusinessException {
		List<Supplier> suppliers = null;
		try {
			suppliers = this.supplierDAO.findAll();
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attepting to get all suppliers", de);
		}
		return suppliers;
	}

	@Override
	public List<PredefinedEntry> getAllCurrencies() throws BusinessException {
		List<PredefinedEntry> currencies = null;
		currencies = commonLogic.findEntriesForParam(CURRENCY_PARAM);
		return currencies;
	}

	@Override
	public void updateSubscription(Subscription subscription) throws BusinessException, ValidationException {
		System.out.println("BusinessLogic: Attempting to update Subscription: " + subscription);
//		subscription.setCostUnit(null);
		//TODO: validate parameter
		validator.validate(subscription);
		try {
			subscriptionDAO.updateSubscription(subscription);
		}
		catch (DAOException de) {
			de.printStackTrace();
			throw new BusinessException("Error when attempting to update Subscription with id: " + subscription.getId(), de);
		}		
	}
//
//	@Override
//	public List<PredefinedEntry> findAllEntriesWithParameter(PredefinedParam param) throws BusinessException {
//		System.out.println("BusinessLogic: Attempting to find all entries with param: " + param);
//		List<PredefinedEntry> entries = null;
//		try {
//			entries = predefinedEntryDAO.findAllWithParameter(param);
//		}
//		catch (DAOException de) {
//			throw new BusinessException("Problem when attempting to get all PredefinedEntries for Parameter: " +  param, de);
//		}
//		return entries;
//	}	

	@Override
	public Purchaser getPurchaserById(int id) throws BusinessException {
		Purchaser purchaser = null;
		try {
			purchaser = purchaserDAO.find(id);
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to find purchaser with id " + id, de);
		}
		return purchaser;
	}

	@Override
	public Supplier getSupplierById(int id) throws BusinessException {
		Supplier supplier = null;
		try {
			supplier = supplierDAO.find(id);
		}
		catch (DAOException de) {
			throw new BusinessException("Problem when attempting to find supplier with id: " + id, de);
		}
		return supplier;
	}

	@Override
	public List<CostUnit> getCostUnitsForLibraryLocation(LibraryLocation location) 
		throws BusinessException 
		{		
			List<CostUnit> costUnitsForLocation = null;
			try {
				costUnitsForLocation = costUnitDAO.findAllForLocation(location);
			}
			catch (DAOException de) {
				throw new BusinessException("Problem when attempting to get all CostUntis for LibraryLocation " + location, de);
			}
			return costUnitsForLocation;
		}
}
